import axios, { AxiosResponse } from 'axios'
import type { AxiosInstance } from 'axios'
// import { ElMessage, ElLoading } from 'element-plus'
import type { RequestConfig, RequestInterceptors, CancelRequestSource } from './types'
import { LocalCache } from '@/utils/cache'

interface LoadingInstance {
  target: any
  count: number
}
const loadingInstance: LoadingInstance = {
  target: null,
  count: 0
}

const closeLoading = () => {
  if (loadingInstance.count > 0) loadingInstance.count -= 1
  if (loadingInstance.count === 0) {
    loadingInstance.target.close()
    loadingInstance.target = null
  }
}

class Request {
  // axios 实例
  private instance: AxiosInstance

  // 拦截器对象
  private interceptorsObj?: RequestInterceptors<AxiosResponse>

  /*
  存放取消方法的集合
  * 在创建请求后将取消请求方法 push 到该集合中
  * 封装一个方法，可以取消请求，传入 url: string|string[]
  * 在请求之前判断同一URL是否存在，如果存在就取消请求
  */
  private cancelRequestSourceList?: CancelRequestSource[]

  /*
  存放所有请求URL的集合
  * 请求之前需要将url push到该集合中
  * 请求完毕后将url从集合中删除
  * 添加在发送请求之前完成，删除在响应之后删除
  */
  private requestUrlList?: string[]

  private showLoading: boolean

  private defaultShowLoading = false

  constructor(config: RequestConfig) {
    this.requestUrlList = []
    this.cancelRequestSourceList = []
    this.instance = axios.create(config)
    this.interceptorsObj = config.interceptors
    this.showLoading = config.showLoading ?? this.defaultShowLoading
    // 拦截器执行顺序 接口请求 -> 实例请求 -> 全局请求 -> 实例响应 -> 全局响应 -> 接口响应
    this.instance.interceptors.request.use((res: RequestConfig) => {
      if (this.showLoading) {
        loadingInstance.count += 1
        if (loadingInstance.count === 1) {
          // loadingInstance.target = ElLoading.service({ fullscreen: true })
        }
      }
      return res
    })

    // 使用实例拦截器
    this.instance.interceptors.request.use(this.interceptorsObj?.requestInterceptors, this.interceptorsObj?.requestInterceptorsCatch)
    this.instance.interceptors.response.use(this.interceptorsObj?.responseInterceptors, this.interceptorsObj?.responseInterceptorsCatch)
    // 全局响应拦截器保证最后执行
    this.instance.interceptors.response.use(
      // 因为我们接口的数据都在res.data下，所以我们直接返回res.data
      (res: AxiosResponse) => {
        // const appStore = useAppStore()
        if (this.showLoading) closeLoading() // 关闭loading
        if (res.data?.statusCode === 200) {
          return res.data
        }
        const logout = [401, 405]
        if (logout.includes(res.data?.statusCode)) {
          LocalCache.clear()
          window.location.href = '/login?redirect=' + window.location.pathname
        }
        // ElMessage({
        //   message: res.data?.desc || '请求失败',
        //   type: 'error'
        // })
        return Promise.reject(res.data)
      },
      (error) => {
        if (this.showLoading) closeLoading() // 关闭loading
        let message = ''
        if (error.code === 'ECONNABORTED' || error.message === 'Network Error') {
          message = '网络错误'
        } else if (error.message.includes('timeout')) {
          message = '请求超时'
        } else if (!window.navigator.onLine) {
          message = '请检查网络连接'
        } else {
          message = '未知错误,请稍后再试'
        }
        console.log(message)

        // ElMessage({
        //   message,
        //   type: 'error'
        // })
        return Promise.reject(error)
      }
    )
  }

  /**
   * @description: 获取指定 url 在 cancelRequestSourceList 中的索引
   * @param {string} url
   * @returns {number} 索引位置
   */
  private getSourceIndex(url: string): number {
    return this.cancelRequestSourceList?.findIndex((item: CancelRequestSource) => {
      return Object.keys(item)[0] === url
    }) as number
  }

  /**
   * @description: 删除 requestUrlList 和 cancelRequestSourceList
   * @param {string} url
   * @returns {*}
   */
  private delUrl(url: string) {
    const urlIndex = this.requestUrlList?.findIndex((u) => u === url)
    const sourceIndex = this.getSourceIndex(url)
    // 删除url和cancel方法
    // eslint-disable-next-line no-unused-expressions
    urlIndex !== -1 && this.requestUrlList?.splice(urlIndex as number, 1)
    // eslint-disable-next-line no-unused-expressions
    sourceIndex !== -1 && this.cancelRequestSourceList?.splice(sourceIndex as number, 1)
  }

  request<T>(config: RequestConfig<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      // 如果我们为单个请求设置拦截器，这里使用单个请求的拦截器
      if (config.interceptors?.requestInterceptors) {
        // eslint-disable-next-line no-param-reassign
        config = config.interceptors.requestInterceptors(config)
      }
      const { url } = config
      // url存在保存取消请求方法和当前请求url
      if (url) {
        this.requestUrlList?.push(url)
        config.cancelToken = new axios.CancelToken((c) => {
          this.cancelRequestSourceList?.push({
            [url]: c
          })
        })
      }
      this.showLoading = config.showLoading ?? this.defaultShowLoading
      this.instance
        .request<any, T>(config)
        .then((res) => {
          // 如果我们为单个响应设置拦截器，这里使用单个响应的拦截器
          if (config.interceptors?.responseInterceptors) {
            // eslint-disable-next-line no-param-reassign
            res = config.interceptors.responseInterceptors(res)
          }

          resolve(res)
        })
        .catch((err: any) => {
          reject(err)
        })
        .finally(() => {
          // eslint-disable-next-line no-unused-expressions
          url && this.delUrl(url)
        })
    })
  }

  // 取消请求
  cancelRequest(url: string | string[]) {
    if (typeof url === 'string') {
      // 取消单个请求
      const sourceIndex = this.getSourceIndex(url)
      // eslint-disable-next-line no-unused-expressions
      sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][url]()
    } else {
      // 存在多个需要取消请求的地址
      url.forEach((u) => {
        const sourceIndex = this.getSourceIndex(u)
        // eslint-disable-next-line no-unused-expressions
        sourceIndex >= 0 && this.cancelRequestSourceList?.[sourceIndex][u]()
      })
    }
  }

  // 取消全部请求
  cancelAllRequest() {
    this.cancelRequestSourceList?.forEach((source) => {
      const key = Object.keys(source)[0]
      source[key]()
    })
  }
}

export default Request
export { RequestConfig, RequestInterceptors }
